package com.wb.spring.boot.autoconfigure.proxy;

import com.wb.spring.boot.autoconfigure.annotation.WbClient;
import com.wb.spring.boot.autoconfigure.stereotype.WbRequestBody;
import com.wb.spring.boot.autoconfigure.stereotype.WbRequestMapping;
import com.wb.spring.boot.autoconfigure.stereotype.WbRequestParam;
import javassist.*;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.implementation.MethodDelegation;
import net.bytebuddy.matcher.ElementMatchers;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.core.DefaultParameterNameDiscoverer;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.*;

/**
 * 调用代理对象
 * 基于ByteBuddy 构建
 *
 * @author deeprado
 * @date 2022/9/2 17:26
 */
public class ByteBuddyProxyFactory {

    static Logger logger = LoggerFactory.getLogger(ByteBuddyProxyFactory.class);

    /**
     * 生成的代理对象名称前缀
     */
    private static final String PROXY_PREFIX = "WbRpcProxy";

    /**
     * 生成的代理对象名称后缀
     */
    private static final String PROXY_SUFFIX = "Impl";

    /**
     * 参数为@WbRequestBody的类型
     */
    private static final String REQUEST_BODY_PARAMETER = "REQUEST_BODY_PARAMETER";

    public static <T> Class<T> buildProxy(Class<T> t, BeanFactory beanFactory) {
        // 获取调用的服务名称
        WbClient annotation = t.getAnnotation(WbClient.class);

        String serviceClassName = t.getName();
        // 是否异步
        boolean async = annotation.async();
        String path = annotation.path();
        int maxAttempts = annotation.maxAttempts();

        // 生成实例，并调用 TODO 增加配置判断
        Class<?> fallback = annotation.fallback();
        Class<?> fallbackFactory = annotation.fallbackFactory();

        // 服务名
        String serviceName = annotation.name();
        // 创建代理类对象
        String implClassName = getImplName(t);

        DynamicType.Unloaded<?> dynamicType = new ByteBuddy()
                .subclass(t)
                .name(implClassName)
                .method(ElementMatchers.isAnnotatedWith(WbRequestMapping.class))
//                .intercept(MethodDelegation.to(ByteBuddyInterceptor.class))
                .intercept(MethodDelegation.to(new ByteBuddyInterceptor(
                        t, serviceName, path, async, maxAttempts, beanFactory, fallback, fallbackFactory
                )))
                .make();

        return (Class<T>) dynamicType.load(ByteBuddyProxyFactory.class.getClassLoader()).getLoaded();
    }


    /**
     * 获取全限定名称
     *
     * @param method
     * @return
     * @throws NotFoundException
     */
    private static String buildMethodFullName(CtMethod method) throws NotFoundException {
        StringBuilder methodFullName = new StringBuilder(method.getName());
        if (method.getParameterTypes() != null) {
            for (CtClass parameterType : method.getParameterTypes()) {
                methodFullName.append(parameterType.getName());
            }
        }

        return methodFullName.toString();
    }

    /**
     * key-方法名称+方法参数类型
     * value-方法参数名称集合
     *
     * @param clazz 要解析的类
     * @return 方法的类对应参数名称
     */
    private static Map<String, List<String>> buildParametersMap(Class<?> clazz) {
        // 同名参数映射使用 此处使用1.8之上的--parameters才能支持，否则只能使用注解映射。
        DefaultParameterNameDiscoverer discoverer = new DefaultParameterNameDiscoverer();
        Map<String, List<String>> parameterMap = new HashMap<>(8);
        for (Method method : clazz.getMethods()) {
            StringBuilder name = new StringBuilder(method.getName());
            List<String> parameterRequestNames = new ArrayList<>();
            if (method.getParameters() != null) {
                for (Parameter parameter : method.getParameters()) {
                    name.append(parameter.getType().getName());
                    WbRequestParam annotation = parameter.getAnnotation(WbRequestParam.class);
                    String parameterRequestName = StringUtils.EMPTY;
                    if (annotation != null) {
                        parameterRequestName = annotation.value();
                    }
                    WbRequestBody wbRequestBody = parameter.getAnnotation(WbRequestBody.class);
                    if (wbRequestBody != null) {
                        parameterRequestName = REQUEST_BODY_PARAMETER;
                    }
                    parameterRequestNames.add(parameterRequestName);
                }
            }

            String[] parameterNames = discoverer.getParameterNames(method);
            if (parameterNames == null) {
                parameterMap.put(name.toString(), parameterRequestNames);
                continue;
            }
            List<String> correctParameterRequestNames = new ArrayList<>();
            for (int i = 0; i < parameterRequestNames.size(); i++) {
                String parameterRequestName = parameterRequestNames.get(i);
                if (StringUtils.EMPTY.equals(parameterRequestName)) {
                    correctParameterRequestNames.add(parameterNames[i]);
                    continue;
                }
                correctParameterRequestNames.add(parameterRequestName);
            }

            parameterMap.put(name.toString(), correctParameterRequestNames);
        }

        return parameterMap;
    }

    /**
     * 获取代理类的名称
     *
     * @param t 实现类类型
     * @return 实现类名称
     */
    private static <T> String getImplName(Class<T> t) {
        return t.getPackage().getName() + "." + PROXY_PREFIX + t.getSimpleName() + PROXY_SUFFIX;
    }

    /**
     * @param t
     * @param <T>
     * @return
     */
    private static <T> String getImplFallbackName(Class<T> t) {
        return t.getPackage().getName() + "." + PROXY_PREFIX + t.getSimpleName() + "Fallback" + PROXY_SUFFIX;
    }

}
